#include "ecv_filter_binarize.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <unistd.h>

typedef struct
{
  int id;
  unsigned char avg;
} binbox;

G_DEFINE_TYPE (EcvFilter_Binarize, ecv_filter_binarize, ECV_TYPE_FILTER);

#define ECV_FILTER_BINARIZE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ECV_TYPE_FILTER_BINARIZE, EcvFilter_BinarizePrivate))

struct _EcvFilter_BinarizePrivate
{
  int xdiv;
  int ydiv;
  int boxh;
  int boxw;
  //  GHashTable * boxes;
  binbox ** boxes;
};

enum
{
  PROP_0,
  PROP_ECV_XDIV,
  PROP_ECV_YDIV
};

void
bin_process (EcvFilter *self)
{
  //  switch (self->priv->bpp)
  //    {
  //      while ()
  //	self->priv->data
  EcvFilter_Binarize * bin = ECV_FILTER_BINARIZE(self);
  //  g_print ("calling plugged process\n");
  unsigned long long px;
  unsigned char * data = ecv_filter_get_data(self);
  int datasz = ecv_filter_get_data_size(self);

  int width;
  int height;
  g_object_get (G_OBJECT (bin),"width", &width, NULL);
  g_object_get (G_OBJECT (bin),"height", &height, NULL);

  //1: count average for each box
  int bx = 0;
  int by = 0;
  int i = 0;
  int j = 0;
  double avg;
  binbox * binb;
  int id=0;
  for (by;by<bin->priv->ydiv;by=by+1)
    {
      bx = 0;
      for (bx;bx<bin->priv->xdiv;bx=bx+1)
	{
	  //in box
	  px = 0;
	  j = 0;

	  binb=bin->priv->boxes[id];
	  //	  binb = (binbox*)g_hash_table_lookup (bin->priv->boxes, &id);

	  for (j;j<bin->priv->boxh;j=j+1)
	    {
	      i=0;
	      for (i;i<bin->priv->boxw*3;i=i+3)
		{
		  px = px + data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3];
		}
	    }
	  avg = px/(bin->priv->boxw*bin->priv->boxh);
	  binb->avg = (unsigned char)avg;
	  id = id + 1;
	  //	  printf ("bx: %i, by: %i; avg = %f\n",bx,by,avg);
	}
    }
}

void 
bin_create (EcvFilter *self)
{
  EcvFilter_Binarize * bin = ECV_FILTER_BINARIZE(self);
  int width;
  int height;
  g_object_get (G_OBJECT (bin),"width", &width, NULL);
  g_object_get (G_OBJECT (bin),"height", &height, NULL);

  bin->priv->boxw = width/bin->priv->xdiv;
  bin->priv->boxh = height/bin->priv->ydiv;

  bin->priv->boxes = malloc (sizeof(binbox)*bin->priv->xdiv*bin->priv->ydiv);
  /*  bin->priv->boxes = g_hash_table_new(g_int_hash, g_int_equal);*/
  int i = 0;
  binbox * binb;
  for (i;i<bin->priv->xdiv*bin->priv->ydiv;i=i+1)
    {
      binb = malloc (sizeof (binb));
      binb->id = i;
      bin->priv->boxes[i]=binb;
      //g_hash_table_insert(bin->priv->boxes, &binb->id, binb);
    }
}

static void
ecv_filter_binarize_set_property (GObject      *object,
                        guint         property_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
  EcvFilter_Binarize *self = ECV_FILTER_BINARIZE (object);

  switch (property_id)
    {
    case PROP_ECV_XDIV:
      self->priv->xdiv = g_value_get_uint (value);
      break;

    case PROP_ECV_YDIV:
      self->priv->ydiv = g_value_get_uint (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
ecv_filter_binarize_get_property (GObject    *object,
                        guint       property_id,
                        GValue     *value,
                        GParamSpec *pspec)
{
  EcvFilter_Binarize *self = ECV_FILTER_BINARIZE (object);

  switch (property_id)
    {
    case PROP_ECV_XDIV:
      g_value_set_uint (value, self->priv->xdiv);
      break;

    case PROP_ECV_YDIV:
      g_value_set_uint (value, self->priv->ydiv);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
      }
}

static void
ecv_filter_binarize_dispose (GObject *gobject)
{
  EcvFilter_Binarize *self = ECV_FILTER_BINARIZE (gobject);

  /* 
   * In dispose, you are supposed to free all types referenced from this
   * object which might themselves hold a reference to self. Generally,
   * the most simple solution is to unref all members on which you own a 
   * reference.
   */

  /* dispose might be called multiple times, so we must guard against
   * calling g_object_unref() on an invalid GObject.
   */

  /*  if (self->priv->)
    {
      g_object_unref (self->priv->an_object);
      }*/

  /* Chain up to the parent class */
  G_OBJECT_CLASS (ecv_filter_binarize_parent_class)->dispose (gobject);
}

static void
ecv_filter_binarize_finalize (GObject *gobject)
{
  EcvFilter_Binarize *self = ECV_FILTER_BINARIZE (gobject);

  //GHashTableIter iter;
  //g_hash_table_iter_init(&iter, self->priv->boxes);
  //  int * key;
  //  binbox * value;
  int i=0;
  //  while (g_hash_table_iter_next (&iter, (void**)&key,
  //  (void**)&value)) 
  for (i;i<self->priv->xdiv*self->priv->ydiv;i=i+1)
    {
      free(self->priv->boxes[i]);
    }
  //  g_hash_table_destroy(self->priv->boxes);
  free(self->priv->boxes);
  
  /* Chain up to the parent class */
  G_OBJECT_CLASS (ecv_filter_binarize_parent_class)->finalize (gobject);
}

static GObject *
ecv_filter_binarize_constructor (GType                  gtype,
                       guint                  n_properties,
                       GObjectConstructParam *properties)
{
  GObject *obj;

  {
    /* Always chain up to the parent constructor */
    EcvFilter_BinarizeClass *klass;
    GObjectClass *parent_class;  
    parent_class = G_OBJECT_CLASS (ecv_filter_binarize_parent_class);
    obj = parent_class->constructor (gtype, n_properties, properties);
  }
  
  /* update the object state depending on constructor properties */

  return obj;
}

static void
ecv_filter_binarize_class_init (EcvFilter_BinarizeClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  GParamSpec *pspec;

  gobject_class->dispose = ecv_filter_binarize_dispose;
  gobject_class->finalize = ecv_filter_binarize_finalize;
  gobject_class->set_property = ecv_filter_binarize_set_property;
  gobject_class->get_property = ecv_filter_binarize_get_property;

  pspec = g_param_spec_uint ("xdiv",
			     "",
			     "x divider",
			     1,
			     65534,
			     4,
			     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
  
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_XDIV,
                                   pspec);

  pspec = g_param_spec_uint ("ydiv",
			     "",
			     "y divider",
			     1,
			     65534,
			     4,
			     G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_YDIV,
                                   pspec);

  
  EcvFilterClass *parent_class;  
  parent_class = ECV_FILTER_CLASS (klass);
  parent_class->process = bin_process;
  parent_class->create = bin_create;
  
  g_type_class_add_private (klass, sizeof (EcvFilter_BinarizePrivate));
  gobject_class->constructor = ecv_filter_binarize_constructor;
}

static void
ecv_filter_binarize_init (EcvFilter_Binarize *self)
{
  self->priv = ECV_FILTER_BINARIZE_GET_PRIVATE (self);

  //  self->priv->an_object = g_object_new (ECV_TYPE_BA, NULL);
}

void ecv_filter_binarize_preparetodraw(EcvFilter_Binarize *self, int griden, int fillen, int thr, int fillclustersen)
{
  EcvFilter_Binarize * bin = self;
  unsigned char * data = ecv_filter_get_data(self);
  int datasz = ecv_filter_get_data_size(self);

  int width;
  int height;
  g_object_get (G_OBJECT (bin),"width", &width, NULL);
  g_object_get (G_OBJECT (bin),"height", &height, NULL);

  //1: count average for each box
  int bx = 0;
  int by = 0;
  int i = 0;
  int j = 0;
  double avg;
  binbox * binb;
  int id = 0;
  unsigned char px;
  int put=0;

  for (by;by<bin->priv->ydiv;by=by+1)
    {
      bx = 0;
      for (bx;bx<bin->priv->xdiv;bx=bx+1)
	{
	  //in box
	  if (fillen==1 || fillclustersen == 1)
	    {
	      //	      binb = (binbox*)g_hash_table_lookup
	      //	      (bin->priv->boxes, &id);
	      binb=bin->priv->boxes[id];
	    }
	  j = 0;
	  for (j;j<bin->priv->boxh;j=j+1)
	    {
	      i = 0;
	      for (i;i<bin->priv->boxw*3;i=i+3)
		{
		  if (griden == 1)
		    {
		      if (j == 0 || j==bin->priv->boxh-1)
			{
			  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3] = 0x00;
			  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+1+j*width*3] = 0x00;
			  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+2+j*width*3] = 0x00;
			}
		      else if (i==0 || i==bin->priv->boxw*3-3)
			{
			  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3] = 0x00;
			  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+1+j*width*3] = 0x00;
			  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+2+j*width*3] = 0x00;
			}
		    }
		  if (fillen==1 || fillclustersen==1)
		    {
		      if (griden==1)
			{
			  if (j > 0 && j<bin->priv->boxh-1 && i>0 && i<bin->priv->boxw*3-3)
			    put = 0;
			  else
			    put = 1;
			}
		      else
			put = 1;
		      
		      if (put == 1)
			{
			  if (fillen==1)
			    {
			      data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3] = binb->avg;
			      data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+1+j*width*3] = binb->avg;
			      data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+2+j*width*3] = binb->avg;
			    }
			  
			  if (fillclustersen == 1)
			    {
			      px = data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3];
			      if (abs(px-binb->avg)>thr)
				{
				  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3] = 0x00;
				  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+1+j*width*3] = 0x00;
				  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+2+j*width*3] = 0x00;
				}
			      else
				{
				  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+j*width*3] = 0xFF;
				  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+1+j*width*3] = 0xFF;
				  data[bx*bin->priv->boxw*3 + by*bin->priv->boxh*width*3+i+2+j*width*3] = 0xFF;
				}
			    }
			}
		    }

		  
		}
	    }
	  id = id + 1;
	}
    }

}
//------------------------------------------------------
/*                     methods                        */

EcvFilter_Binarize * 
ecv_filter_binarize_new (int width, int height, int bpp, int xdiv, int ydiv)
{
  return g_object_new (ECV_TYPE_FILTER_BINARIZE,
		       "width", width,
		       "height", height,
		       "bpp", bpp,
		       "xdiv", xdiv,
		       "ydiv", ydiv,
		       NULL);
}
